package com.jsh.erp.service.accountHead;

import com.alibaba.fastjson.JSONObject;
import com.jsh.erp.constants.BusinessConstants;
import com.jsh.erp.constants.ChequeEnum;
import com.jsh.erp.constants.ExceptionConstants;
import com.jsh.erp.dao.mappers.*;
import com.jsh.erp.domain.entities.*;
import com.jsh.erp.domain.param.CashReportParam;
import com.jsh.erp.domain.vo.AccountItemVo4List;
import com.jsh.erp.domain.vo.CashReport;
import com.jsh.erp.exception.BusinessRunTimeException;
import com.jsh.erp.service.accountItem.AccountItemService;
import com.jsh.erp.service.depotHead.DepotHeadService;
import com.jsh.erp.service.log.LogService;
import com.jsh.erp.service.orgaUserRel.OrgaUserRelService;
import com.jsh.erp.service.supplier.SupplierService;
import com.jsh.erp.service.user.UserService;
import com.jsh.erp.utils.ExceptionUtil;
import com.jsh.erp.utils.StringUtil;
import com.jsh.erp.utils.TokenUtil;
import com.jsh.erp.utils.Tools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static com.jsh.erp.utils.Tools.getCenternTime;

@Service
public class AccountHeadService {
    private Logger logger = LoggerFactory.getLogger(AccountHeadService.class);
    @Resource
    private AccountHeadMapper accountHeadMapper;
    @Resource
    private AccountHeadMapperEx accountHeadMapperEx;
    @Resource
    private OrgaUserRelService orgaUserRelService;
    @Resource
    private AccountItemService accountItemService;
    @Resource
    private DepotHeadService depotHeadService;
    @Resource
    private UserService userService;
    @Resource
    private SupplierService supplierService;
    @Resource
    private LogService logService;
    @Resource
    private AccountItemMapperEx accountItemMapperEx;
    @Resource
    private ChequeBillMapMapper chequeBillMapMapper;
    @Resource
    private ChequeBillMapMapperEx chequeBillMapMapperEx;
    @Resource
    private DepotItemMapperEx depotItemMapperEx;
    @Autowired
    private ChequeMapper chequeMapper;


    public AccountHead getAccountHead(long id) throws Exception {
        AccountHead result = null;
        try {
            result = accountHeadMapper.selectByPrimaryKey(id);
        } catch (Exception e) {
            ExceptionUtil.readFail(logger, e);
        }
        return result;
    }

    public List<AccountHead> getAccountHeadListByIds(String ids) throws Exception {
        List<Long> idList = StringUtil.strToLongList(ids);
        List<AccountHead> list = new ArrayList<>();
        try {
            AccountHeadExample example = new AccountHeadExample();
            example.createCriteria().andIdIn(idList);
            list = accountHeadMapper.selectByExample(example);
        } catch (Exception e) {
            ExceptionUtil.readFail(logger, e);
        }
        return list;
    }

    public List<AccountHead> getAccountHead() throws Exception {
        AccountHeadExample example = new AccountHeadExample();
        List<AccountHead> list = null;
        try {
            list = accountHeadMapper.selectByExample(example);
        } catch (Exception e) {
            ExceptionUtil.readFail(logger, e);
        }
        return list;
    }


    public List<AccountHeadVo4ListEx> select(String type, String billNo, String beginTime, String endTime,
                                             Long organId, Long creator, Long handsPersonId, Long accountId, String status,
                                             String remark, String number, int offset, int rows) throws Exception {
        List<AccountHeadVo4ListEx> resList = new ArrayList<>();
        try {
            String[] creatorArray = getCreatorArray();
            beginTime = Tools.parseDayToTime(beginTime, BusinessConstants.DAY_FIRST_TIME);
            endTime = Tools.parseDayToTime(endTime, BusinessConstants.DAY_LAST_TIME);
            List<AccountHeadVo4ListEx> list = accountHeadMapperEx.selectByConditionAccountHead(type, creatorArray, billNo,
                    beginTime, endTime, organId, creator, handsPersonId, accountId, status, remark, number, offset, rows);
            if (null != list) {
                for (AccountHeadVo4ListEx ah : list) {
                    if (ah.getChangeAmount() != null) {
                        if (BusinessConstants.TYPE_MONEY_IN.equals(ah.getType())) {
                            ah.setChangeAmount(ah.getChangeAmount());
                        } else if (BusinessConstants.TYPE_MONEY_OUT.equals(ah.getType())) {
                            ah.setChangeAmount(BigDecimal.ZERO.subtract(ah.getChangeAmount()));
                        } else {
                            ah.setChangeAmount(ah.getChangeAmount().abs());
                        }
                    }
                    if (ah.getTotalPrice() != null) {
                        if (BusinessConstants.TYPE_MONEY_IN.equals(ah.getType())) {
                            ah.setTotalPrice(ah.getTotalPrice());
                        } else if (BusinessConstants.TYPE_MONEY_OUT.equals(ah.getType())) {
                            ah.setTotalPrice(BigDecimal.ZERO.subtract(ah.getTotalPrice()));
                        } else {
                            ah.setTotalPrice(ah.getTotalPrice().abs());
                        }
                    }
                    if (ah.getBillTime() != null) {
                        ah.setBillTimeStr(getCenternTime(ah.getBillTime()));
                    }
                    List<AccountItemVo4List> detailList = accountItemService.getDetailList(ah.getId());
                    if (!CollectionUtils.isEmpty(detailList)) {
                        for (AccountItemVo4List accountItemVo : detailList) {
                            if (!StringUtil.isEmpty(ah.getInvoiceNo())) {
                                ah.setInvoiceNo(ah.getInvoiceNo() + " , ");
                            } else {
                                ah.setInvoiceNo("");
                            }
                            ah.setInvoiceNo(ah.getInvoiceNo() + accountItemVo.getBillNumber());
                        }
                    }

                    resList.add(ah);
                }
            }
        } catch (Exception e) {
            ExceptionUtil.readFail(logger, e);
        }
        return resList;
    }

    public Long countAccountHead(String type, String billNo, String beginTime, String endTime,
                                 Long organId, Long creator, Long handsPersonId, Long accountId, String status,
                                 String remark, String number) throws Exception {
        Long result = null;
        try {
            String[] creatorArray = getCreatorArray();
            beginTime = Tools.parseDayToTime(beginTime, BusinessConstants.DAY_FIRST_TIME);
            endTime = Tools.parseDayToTime(endTime, BusinessConstants.DAY_LAST_TIME);
            result = accountHeadMapperEx.countsByAccountHead(type, creatorArray, billNo,
                    beginTime, endTime, organId, creator, handsPersonId, accountId, status, remark, number);
        } catch (Exception e) {
            ExceptionUtil.readFail(logger, e);
        }
        return result;
    }

    /**
     * 根据角色类型获取操作员数组
     *
     * @return
     * @throws Exception
     */
    private String[] getCreatorArray() throws Exception {
        String creator = "";
        User user = userService.getCurrentUser();
        String roleType = userService.getRoleTypeByUserId(user.getId()).getType(); //角色类型
        if (BusinessConstants.ROLE_TYPE_PRIVATE.equals(roleType)) {
            creator = user.getId().toString();
        } else if (BusinessConstants.ROLE_TYPE_THIS_ORG.equals(roleType)) {
            creator = orgaUserRelService.getUserIdListByUserId(user.getId());
        }
        String[] creatorArray = null;
        if (StringUtil.isNotEmpty(creator)) {
            creatorArray = creator.split(",");
        }
        return creatorArray;
    }

    @Transactional(value = "transactionManager", rollbackFor = Exception.class)
    public int insertAccountHead(JSONObject obj, HttpServletRequest request) throws Exception {
        AccountHead accountHead = JSONObject.parseObject(obj.toJSONString(), AccountHead.class);
        int result = 0;
        try {
            User userInfo = userService.getCurrentUser();
            accountHead.setCreator(userInfo == null ? null : userInfo.getId());
            result = accountHeadMapper.insertSelective(accountHead);
            logService.insertLog("财务",
                    new StringBuffer(BusinessConstants.LOG_OPERATION_TYPE_ADD).append(accountHead.getBillNo()).toString(), request);
        } catch (Exception e) {
            ExceptionUtil.writeFail(logger, e);
        }
        return result;
    }

    @Transactional(value = "transactionManager", rollbackFor = Exception.class)
    public int updateAccountHead(JSONObject obj, HttpServletRequest request) throws Exception {
        AccountHead accountHead = JSONObject.parseObject(obj.toJSONString(), AccountHead.class);
        int result = 0;
        try {
            result = accountHeadMapper.updateByPrimaryKeySelective(accountHead);
            logService.insertLog("财务",
                    new StringBuffer(BusinessConstants.LOG_OPERATION_TYPE_EDIT).append(accountHead.getBillNo()).toString(), request);
        } catch (Exception e) {
            ExceptionUtil.writeFail(logger, e);
        }
        return result;
    }

    @Transactional(value = "transactionManager", rollbackFor = Exception.class)
    public int deleteAccountHead(Long id, HttpServletRequest request) throws Exception {
        return batchDeleteAccountHeadByIds(id.toString());
    }

    @Transactional(value = "transactionManager", rollbackFor = Exception.class)
    public int batchDeleteAccountHead(String ids, HttpServletRequest request) throws Exception {
        return batchDeleteAccountHeadByIds(ids);
    }

    @Transactional(value = "transactionManager", rollbackFor = Exception.class)
    public int batchDeleteAccountHeadByIds(String ids) throws Exception {
        StringBuffer sb = new StringBuffer();
        sb.append(BusinessConstants.LOG_OPERATION_TYPE_DELETE);
        User userInfo = userService.getCurrentUser();
        String[] idArray = ids.split(",");
        //删除主表
        accountItemMapperEx.batchDeleteAccountItemByHeadIds(new Date(), userInfo == null ? null : userInfo.getId(), idArray);
        //删除子表
        accountHeadMapperEx.batchDeleteAccountHeadByIds(new Date(), userInfo == null ? null : userInfo.getId(), idArray);
        //删除支票关系表
        List<String> idList = StringUtil.strToStringList(ids);
        chequeBillMapMapperEx.delByNumber(idList);
        List<AccountHead> list = getAccountHeadListByIds(ids);
        for (AccountHead accountHead : list) {
            sb.append("[").append(accountHead.getBillNo()).append("]");
            if (1 == accountHead.getStatus() && !TokenUtil.parseTokenFromRequest().isAdmin()) {
                throw new BusinessRunTimeException(ExceptionConstants.ACCOUNT_HEAD_UN_AUDIT_DELETE_FAILED_CODE,
                        String.format(ExceptionConstants.ACCOUNT_HEAD_UN_AUDIT_DELETE_FAILED_MSG));
            }
            if ("收预付款".equals(accountHead.getType())) {
                if (accountHead.getOrganId() != null) {
                    //更新会员预付款
                    supplierService.updateAdvanceIn(accountHead.getOrganId());
                }
            }
        }
        logService.insertLog("财务", sb.toString(),
                ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest());
        return 1;
    }

    /**
     * 校验单据编号是否存在
     *
     * @param id
     * @param billNo
     * @return
     * @throws Exception
     */
    public int checkIsBillNoExist(Long id, String billNo) throws Exception {
        AccountHeadExample example = new AccountHeadExample();
        example.createCriteria().andIdNotEqualTo(id).andBillNoEqualTo(billNo).andDeleteFlagNotEqualTo(BusinessConstants.DELETE_FLAG_DELETED);
        List<AccountHead> list = null;
        try {
            list = accountHeadMapper.selectByExample(example);
        } catch (Exception e) {
            ExceptionUtil.readFail(logger, e);
        }
        return list == null ? 0 : list.size();
    }

    @Transactional(value = "transactionManager", rollbackFor = Exception.class)
    public int batchSetStatus(Integer status, String accountHeadIds) throws Exception {
        int result = 0;
        try {
            List<Long> ahIds = new ArrayList<>();
            List<Long> ids = StringUtil.strToLongList(accountHeadIds);
            for (Long id : ids) {
                AccountHead accountHead = getAccountHead(id);
                if (0 == status) {
                    if (1 == accountHead.getStatus()) {
                        ahIds.add(id);
                    }
                } else if (1 == status) {
                    if (0 == accountHead.getStatus()) {
                        ahIds.add(id);
                    }
                }
            }
            if (ahIds.size() > 0) {
                AccountHead accountHead = new AccountHead();
                accountHead.setStatus(status);
                AccountHeadExample example = new AccountHeadExample();
                example.createCriteria().andIdIn(ahIds);
                result = accountHeadMapper.updateByExampleSelective(accountHead, example);
            } else {
                return 1;
            }
        } catch (Exception e) {
            ExceptionUtil.writeFail(logger, e);
        }
        return result;
    }

    @Transactional(value = "transactionManager", rollbackFor = Exception.class)
    public void addAccountHeadAndDetail(String beanJson, String rows, HttpServletRequest request) throws Exception {
        AccountHead accountHead = JSONObject.parseObject(beanJson, AccountHead.class);
        //校验单号是否重复
        if (checkIsBillNoExist(0L, accountHead.getBillNo()) > 0) {
            throw new BusinessRunTimeException(ExceptionConstants.ACCOUNT_HEAD_BILL_NO_EXIST_CODE,
                    String.format(ExceptionConstants.ACCOUNT_HEAD_BILL_NO_EXIST_MSG));
        }
        User userInfo = userService.getCurrentUser();
        accountHead.setCreator(userInfo == null ? null : userInfo.getId());
        if (accountHead.getStatus() == null) {
            accountHead.setStatus(BusinessConstants.BILLS_STATUS_UN_AUDIT);
        }
        accountHeadMapper.insertSelective(accountHead);
        //根据单据编号查询单据id
        AccountHeadExample dhExample = new AccountHeadExample();
        dhExample.createCriteria().andBillNoEqualTo(accountHead.getBillNo()).andDeleteFlagNotEqualTo(BusinessConstants.DELETE_FLAG_DELETED);
        List<AccountHead> list = accountHeadMapper.selectByExample(dhExample);
        Long headId = null;
        if (list != null) {
            headId = list.get(0).getId();
            String type = list.get(0).getType();
            /**处理单据子表信息*/
            accountItemService.saveDetials(rows, headId, type, request);
        }
        if ("收预付款".equals(accountHead.getType())) {
            //更新会员预付款
            supplierService.updateAdvanceIn(accountHead.getOrganId());
        }
        //更新收款单和支票映射关系
        if (StringUtil.isNotEmpty(accountHead.getChequeNumber())) {
            List<String> numbersList = Arrays.stream(accountHead.getChequeNumber().split(","))
                    .collect(Collectors.toList());
            for (String chequeNo : numbersList) {
                ChequeBillMapExample chequeBillMapExample = new ChequeBillMapExample();
                chequeBillMapExample.createCriteria().andChequeNumberEqualTo(chequeNo).andNumEqualTo(accountHead.getBillNo());
                List<ChequeBillMap> chequeBillMaps = chequeBillMapMapper.selectByExample(chequeBillMapExample);
                if (chequeBillMaps.size() > 0) {
                    continue;
                }
                ChequeBillMap chequeBillMap = new ChequeBillMap();
                chequeBillMap.setChequeNumber(chequeNo);
                chequeBillMap.setNum(accountHead.getBillNo());
                chequeBillMap.setType(1);
                chequeBillMap.setBillId(headId);
                chequeBillMap.setUpdatedBy(TokenUtil.parseTokenFromRequest().getUserId());
                chequeBillMapMapper.insertSelective(chequeBillMap);
            }
        }
        logService.insertLog("财务单据",
                new StringBuffer(BusinessConstants.LOG_OPERATION_TYPE_ADD).append(accountHead.getBillNo()).toString(), request);
    }

    @Transactional(value = "transactionManager", rollbackFor = Exception.class)
    public void updateAccountHeadAndDetail(String beanJson, String rows, HttpServletRequest request) throws Exception {
        AccountHead accountHead = JSONObject.parseObject(beanJson, AccountHead.class);
        //校验单号是否重复
        if (checkIsBillNoExist(accountHead.getId(), accountHead.getBillNo()) > 0) {
            throw new BusinessRunTimeException(ExceptionConstants.ACCOUNT_HEAD_BILL_NO_EXIST_CODE,
                    String.format(ExceptionConstants.ACCOUNT_HEAD_BILL_NO_EXIST_MSG));
        }
        AccountHead oldHead = accountHeadMapper.selectByPrimaryKey(accountHead.getId());
        accountHeadMapper.updateByPrimaryKeySelective(accountHead);
        //更新收款单和支票映射关系
        if ("收款".equals(oldHead.getType()) && !Objects.equals(oldHead.getChequeNumber(), accountHead.getChequeNumber())) {
            if (StringUtil.isNotEmpty(oldHead.getChequeNumber())) {
                List<String> numbersList = Arrays.stream(oldHead.getChequeNumber().split(","))
                        .collect(Collectors.toList());
                for (String chequeNo : numbersList) {
                    chequeBillMapMapperEx.delByChequeNumber(chequeNo);
                }
            }
            if (StringUtil.isNotEmpty(accountHead.getChequeNumber())) {
                List<String> numbersList = Arrays.stream(accountHead.getChequeNumber().split(","))
                        .collect(Collectors.toList());
                for (String chequeNo : numbersList) {
                    ChequeBillMapExample chequeBillMapExample = new ChequeBillMapExample();
                    chequeBillMapExample.createCriteria().andChequeNumberEqualTo(chequeNo).andNumEqualTo(oldHead.getBillNo());
                    List<ChequeBillMap> chequeBillMaps = chequeBillMapMapper.selectByExample(chequeBillMapExample);
                    if (chequeBillMaps.size() > 0) {
                        continue;
                    }
                    ChequeBillMap chequeBillMap = new ChequeBillMap();
                    chequeBillMap.setChequeNumber(chequeNo);
                    chequeBillMap.setNum(oldHead.getBillNo());
                    chequeBillMap.setType(1);
                    chequeBillMap.setBillId(accountHead.getId());
                    chequeBillMap.setUpdatedBy(TokenUtil.parseTokenFromRequest().getUserId());
                    chequeBillMapMapper.insertSelective(chequeBillMap);
                }
            }
        }

        //根据单据编号查询单据id
        AccountHeadExample dhExample = new AccountHeadExample();
        dhExample.createCriteria().andBillNoEqualTo(accountHead.getBillNo()).andDeleteFlagNotEqualTo(BusinessConstants.DELETE_FLAG_DELETED);
        List<AccountHead> list = accountHeadMapper.selectByExample(dhExample);
        if (list != null) {
            Long headId = list.get(0).getId();
            String type = list.get(0).getType();
            /**处理单据子表信息*/
            accountItemService.saveDetials(rows, headId, type, request);
        }
        if ("收预付款".equals(accountHead.getType())) {
            //更新会员预付款
            supplierService.updateAdvanceIn(accountHead.getOrganId());
        }
        logService.insertLog("财务单据",
                new StringBuffer(BusinessConstants.LOG_OPERATION_TYPE_EDIT).append(accountHead.getBillNo()).toString(), request);
    }

    public List<AccountHeadVo4ListEx> getDetailByNumber(String billNo) throws Exception {
        String lang = userService.getCurrentUser().getLang();
        boolean isEn = false;
        if ("en_US".equals(lang)) {
            isEn = true;
        }
        List<AccountHeadVo4ListEx> resList = new ArrayList<AccountHeadVo4ListEx>();
        List<AccountHeadVo4ListEx> list = null;
        try {
            list = accountHeadMapperEx.getDetailByNumber(billNo);
        } catch (Exception e) {
            ExceptionUtil.readFail(logger, e);
        }
        if (null != list) {
            for (AccountHeadVo4ListEx ah : list) {
                if (ah.getChangeAmount() != null) {
                    if (BusinessConstants.TYPE_MONEY_IN.equals(ah.getType())) {
                        ah.setChangeAmount(ah.getChangeAmount());
                    } else if (BusinessConstants.TYPE_MONEY_OUT.equals(ah.getType())) {
                        ah.setChangeAmount(BigDecimal.ZERO.subtract(ah.getChangeAmount()));
                    } else {
                        ah.setChangeAmount(ah.getChangeAmount().abs());
                    }
                }
                if (ah.getTotalPrice() != null) {
                    if (BusinessConstants.TYPE_MONEY_IN.equals(ah.getType())) {
                        ah.setTotalPrice(ah.getTotalPrice());
                    } else if (BusinessConstants.TYPE_MONEY_OUT.equals(ah.getType())) {
                        ah.setTotalPrice(BigDecimal.ZERO.subtract(ah.getTotalPrice()));
                    } else {
                        ah.setTotalPrice(ah.getTotalPrice().abs());
                    }
                }
                if (ah.getBillTime() != null) {
                    ah.setBillTimeStr(getCenternTime(ah.getBillTime()));
                }
                List<String> idList = StringUtil.strToStringList(ah.getChequeNumber());
                if (!CollectionUtils.isEmpty(idList)) {
                    ChequeExample chequeExample = new ChequeExample();
                    chequeExample.createCriteria().andChequeNumberIn(idList).andDeleteFlagNotEqualTo(1);
                    List<Cheque> cheques = chequeMapper.selectByExample(chequeExample);
                    String chequeInfo = "";
                    for (int i = 0; i < cheques.size(); i++) {
                        Cheque cheque = cheques.get(i);
                        chequeInfo = chequeInfo + cheque.getChequeNumber() + "(" + ChequeEnum.getNameByStatus(cheque.getStatus(), isEn) + ")";
                        if (i != cheques.size() - 1) {
                            chequeInfo = chequeInfo + ", ";
                        }
                    }
                    ah.setChequeInfo(chequeInfo);
                }
                resList.add(ah);
            }
        }
        return resList;
    }

    public List<AccountItem> getFinancialBillNoByBillIdList(List<Long> idList) {
        return accountHeadMapperEx.getFinancialBillNoByBillIdList(idList);
    }

    public List<AccountHead> getFinancialBillNoByBillId(Long billId) {
        return accountHeadMapperEx.getFinancialBillNoByBillId(billId);
    }

    //出库单可能会绑定多个付款单
    public SaleSumAccountHead getAccountHeadBySaleId(Long billId) {
        List<SaleSumAccountHead> accountHeadBySaleList = accountHeadMapperEx.getAccountHeadBySaleId(billId);
        if (CollectionUtils.isEmpty(accountHeadBySaleList)) {
            return new SaleSumAccountHead();
        }
        return accountHeadBySaleList.get(0);
    }

    public List<CashReport> getCashReport(CashReportParam param) {
        if (!StringUtils.isEmpty(param.getBeginTime())) {
            param.setBeginTime(Tools.parseDayToTime(param.getBeginTime(), BusinessConstants.DAY_FIRST_TIME));
        }
        if (!StringUtils.isEmpty(param.getEndTime())) {
            param.setEndTime(Tools.parseDayToTime(param.getEndTime(), BusinessConstants.DAY_LAST_TIME));
        }
        List<CashReport> list = accountHeadMapperEx.getCashReport(param);
        if (!CollectionUtils.isEmpty(list)) {
            for (CashReport cashReport : list) {
                if (cashReport.getBillId() != null) {
                    List<DepotItemVo4WithInfoEx> detailList = depotItemMapperEx.getDetailList(Long.valueOf(cashReport.getBillId()));
                    if (!CollectionUtils.isEmpty(detailList)) {
                        cashReport.setMaterialInfo(detailList.get(0).getMName());
                    }
                }
            }
        }
        return list;
    }


    public Long countCashReport(CashReportParam param) {

        Long l = accountHeadMapperEx.countCashReport(param);
        return l == null ? 0 : l;
    }
}
